home *** CD-ROM | disk | FTP | other *** search
-
- How Things Are Supposed To Be Done
- ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-
- This is just a quick guide to help you write Software That Works (TM).
- This document is only a draft at the moment, so please report errors and
- improvements to me (lcs@lysator.liu.se). Please use common sence while reading
- it, there are probably errors.
-
-
-
-
- 1. Opening and closing 'ahi.device' for low-level access.
-
- Not too hard. Just open 'ahi.device' unit 'AHI_NO_UNIT'. After that
- you can access all the functions of the device just as if you had
- opened a standard shared library.
-
- For the assembler programmer I have written two macros; OPENAHI and
- CLOSEAHI. Here is a small example:
-
- OPENAHI 2 ;Open at least version 2 of 'ahi.device'.
- lea _AHIBase(pc),a0
- move.l d0,(a0)
- beq error
-
- ; AHIs functions can now be called as normal library functions:
- move.l _AHIBase(pc),a6
- moveq #AHI_INVALID_ID,d0
- jsr _LVOAHI_NextAudioID
-
- error:
- CLOSEAHI
- rts
-
- Note that you HAVE to execute the CLOSEAHI macro even if OPENAHI failed!
-
-
- For the C programmer, here is how it should be done:
-
- struct Library *AHIBase;
- struct MsgPort *AHImp=NULL;
- struct AHIRequest *AHIio=NULL;
- BYTE AHIDevice=-1;
-
- if(AHImp=CreateMsgPort())
- {
- if(AHIio=(struct AHIRequest *)CreateIORequest(AHImp,sizeof(struct AHIRequest)))
- {
- AHIio->ahir_Version=2; // Open at least version 2 of 'ahi.device'.
- if(!(AHIDevice=OpenDevice(AHINAME,AHI_NO_UNIT,(struct IORequest *)AHIio,NULL)))
- {
- AHIBase=(struct Library *)AHIio->ahir_Std.io_Device;
-
- // AHIs functions can now be called as normal library functions:
- AHI_NextAudioID(AHI_INVALID_ID);
-
- CloseDevice((struct IORequest *)AHIio);
- AHIDevice=-1; // Good habit, IMHO.
- }
- DeleteIORequest((struct IORequest *)AHIio);
- AHIio=NULL;
- }
- DeleteMsgPort(AHImp);
- AHImp=NULL;
- }
-
-
-
-
- 2.1 Getting and releasing control of the hardware.
-
- If you wish to call other functions than
- AHI_AllocAudioRequestA()
- AHI_AudioRequestA()
- AHI_BestAudioIDA()
- AHI_FreeAudioRequest()
- AHI_GetAudioAttrsA()
- AHI_NextAudioID()
- you have to allocate the actual sound hardware. This is done with
- AHI_AllocAudioA(). AHI_AllocAudioA() returns an AHIAudioCtrl structure,
- or NULL if the hareware could not be allocated. The AHIAudioCtrl
- structure has only one public field, ahiac_UserData. This is for
- you use, you may read and write it at will.
- If AHI_AllocAudioA() fails, it is important that you handle the
- situation gracefully.
-
- When you have finished playing or recording audio, call AHI_FreeAudio() to
- deallocate the hardware and other resources allocated by AHI_AllocAudioA().
-
-
- 2.2 AHI_AllocAudioA() tags.
-
- AHI_AllocAudioA() takes several tags as input.
-
- AHIA_AudioID: This is the audio mode to be used. You must not use
- any hardcoded values other than AHI_DEFAULT_ID, which is the user's
- default fallback ID. AHI_AllocAudioA() is the only function that will
- accepts AHI_DEFAULT_ID, so don't try to use it with others. In most
- cases you should ask the user for an ID code (with AHI_AudioRequestA())
- and then store the value in your settings file.
- AHIA_MixFreq: This is the mixing frequency to be used. The actual
- frequency will be rounded to the nearest frequency supported by the
- sound hardware. To find the actual frequency, use AHI_GetAudioAttrsA().
- If omitted or AHI_DEFAULT_FREQ, the user's preferred fallback
- frequency will be used. In most cases you should ask the user for an ID
- code (with AHI_AudioRequestA()) and then store the value in your
- settings file.
- AHIA_Channels: All sounds are played on a channel, this tag selects how
- many you wish to use. In general it takes more CPU power the more
- channels you use, and the volume gets lower and lower.
- AHIA_Sounds: You must tell AHI how many different samples/sounds you
- are going to play. See section 3 for more information.
- AHIA_SoundFunc: With this tag you tell AHI to call a Hook when a sound
- has been started. It works just like Paulas audio interrupts. The
- Hook recieves a AHISoundMessage structure as message.
- AHISoundMessage->ahism_Channel indicates which channel the sound that
- caused the Hook to be called is played on.
- AHIA_PlayerFunc: If you are going to play a musical score, you should
- use this "interupt" source instead of VBLANK or CIA timers in order
- to get the best result for all audio drivers. If you cannot use this,
- you must not use any 'non-realtime' modes (see AHI_GetAudioAttrsA()
- in the autodocs (the AHIDB_Realtime tag)).
- AHIA_PlayerFreq: If non-zero, enables timing and specifies how many times
- per second PlayerFunc will be called. This must be specified if
- AHIA_PlayerFunc is! It is suggested that you keep the frequency below
- 100-200 Hz.
- AHIA_MinPlayerFreq: The minimum frequency (AHIA_PlayerFreq) you will
- use. You should always supply this if you are using the device's
- interrupt feature!
- AHIA_MaxPlayerFreq: The maximum frequency (AHIA_PlayerFreq) you will
- use. You should always supply this if you are using the device's
- interrupt feature!
- AHIA_RecordFunc: This hook will be called regulary when sampling is
- turned on (see AHI_ControlAudioA()). It is important that you always
- check the format of the sampled data, and ignore it if you can't
- parse it. Since this hook may be called from an interrupt, it is not
- legal to directly Write() the buffer to disk. To record directly to
- harddisk you'll have to copy the samples to another buffer and signal
- a process to save it. To find out the needed size of the buffer, see
- AHI_GetAudioAttrsA() in the autodocs (the AHIDB_MaxRecordSamples tag).
- AHIA_UserData: Can be used to initialise the ahiac_UserData field.
- You do not have to use this tag to change ahiac_UserData, you may
- write to it directly.
-
-
-
-
- 3. Loading and unloading sounds.
-
- Before you can play a sample array, you must AHI_LoadSound() it. Why? Because
- if AHI knows what kind of sounds that will be played later, tables and
- stuff can be set up in advance. Some drivers may even upload the samples
- to the sound cards local RAM and play all samples from there, drastically
- reducing CPU and bus load.
- AHI_LoadSound() also associates each sound or sample with a number,
- which is later used to refer to that particular sound.
- There are 3 type of sounds, namely AHIST_SAMPLE, AHIST_DYNAMICSAMPLE and
- AHIST_INPUT.
- AHIST_SAMPLE: This is used for static samples. Most sounds that will be
- played are of this type. Once the sample has been 'loaded', you may
- not alter the memory where the sample is located.
- AHIST_DYNAMICSAMPLE: If you wish to play a sample that you calculate in
- realtime, or load in portions from disk, you must use this type.
- These samples will never be uploaded to a sound cards local RAM, but
- always played from the normal memory. There is a catch, however.
- Because of the fact that the sound is mixed in chunks, you must have
- a certain number of samples in memory before you start a sound of
- this type. To calculate the size of the buffer (in samples), use the
- following formula:
- size = samples * Fs / Fm
- where samples is the value retured from AHI_GetAudioAttrsA() when
- called with the AHIDB_MaxPlaySamples tag, Fs is the highest frequency
- the sound will be played at and Fm is the actual mixing frequency
- (AHI_ControlAudioA()/AHIC_MixFreq_Query).
- AHIST_INPUT: This sound type is used if you want to hear what you are
- sampling. [It does not work yet.]
-
- If you know that you wont use a sound anymore, call AHI_UnloadSound().
- AHI_FreeAudio() will also do that for you for any sounds left when called.
-
-
-
-
- 4. Playing a sound.
-
- After you have allocated the sound hardware and declared all your
- samples, we're ready to start playback. This is done with a call to
- AHI_ControlAudioA(), with the AHIC_Play tag set to TRUE. When this
- function returns the PlayerFunc (See AHI_AllocAudioA()) is active, and
- the audio driver is feeding zeros to the sound hardware. All you have
- to do now is to set the desired sound, it's frequency and volume. This is
- done with AHI_SetSound(), SetFreq() and SetVol(). Make sure the AHISB_IMM
- bit is set for all these functions flag argument. And don't try to modify
- a channel that is out of range. If you have allocated 4 channels you may
- only modify channels 0-3.
- The sound will not start until both AHI_SetSound() and SetFreq() has
- been called. The sound will play even if SetVol() was not called, but it
- will play completely silent.
- When the sound has been started it will play till the end and then
- repeat again. In order to play a one-shot sample you'll have to install a
- sample interrupt, using the AHIA_SoundFunc tag with AHI_AllocAudioA().
- For more information about using sample interrupt, see section 5.
-
- A little note regarding AHI_SetSound(). Offset is the first sample that
- will be played, both when playing backwards and forwards. This means that
- if you call AHI_SetSound() with offset 0 and lengh 4, sample 0,1,2 and 3
- will be played. If you call AHI_SetSound() with offset 3 and length -4,
- sample 3,2,1 and 0 will be played.
-
-
-
-
- 5. Using sample interrupts.
-
- Earlier versions of AHI had support for loops in sounds, but this support
- has been dropped as of this version (temporary?). To play a one-shot
- sample or an instrument where the one-shot part and the repeat part is
- not equal, a sample interrupt needs to be used. AHIs SoundFunc works like
- Paulas interrupts and is very easy to use.
- The SoundFunc Hook will be called with an AHIAudioCtrl structure as
- object and an AHISoundMessage structure as message. ahism_Channel
- indicates which channel caused the hook to be called.
- An example SoundFunc which handles the repeat part of an instrument can
- look like this:
-
- __asm __saveds ULONG SoundFunc(register __a0 struct Hook *hook,
- register __a2 struct AHIAudioCtrl *actrl,
- register __a1 struct AHISoundMessage *chan)
- {
- if(ChannelDatas[chan->ahism_Channel].Length)
- AHI_SetSound(chan->ahism_Channel,0,
- (ULONG) ChannelDatas[chan->ahism_Channel].Address,
- ChannelDatas[chan->ahism_Channel].Length,
- actrl,NULL);
- else
- AHI_SetSound(chan->ahism_Channel,AHI_NOSOUND,NULL,NULL,actrl,NULL);
- return NULL;
- }
-
- This example is from the AHI NotePlayer for DeliTracker 2. ChannelDatas
- is an array where the start and length of the repeat part is stored. A
- repeat length of zero indicates a one-shot sound. Note that this
- particular example only uses one sound (0). For applications using
- multiple sounds, the sound number would have to be stored in the array as
- well.
- Note that the AHISF_IMM flag should NEVER be set in a SoundFunc Hook!
-